/*
	Programação Concorrente
	Trabalho 2
	Turma A

	Adilson Abreu Couto Rapozo Junior ( n. USP: 6402336 )
	Luiz Alfredo Bertoni ( n. USP: 6418834 )
	Rodrigo Fernandes da Costa ( n. USP: 6793073 )
*/

#include "main.h"

void firstProcess ( int numberOfProcess , int argc , char ** argv ) {

	char * pFileBuffer ; /* The file data in memory. */
	int fileBufferSize ; /* The size of the file. */

	int i ;

	int word ; /* Flag to check for palindrome in words. */
	int phrase ; /* Flag to check for palindrome in phrases. */
	int prime ; /* Flag to check for prime numbers. */
	char pFileName [ FILE_NAME_SIZE ] = { '\0' } ; /* The file name string. */
	int isArgumentsOk ; /* Flag to indicate if the arguments from the command line are ok. */

	isArgumentsOk = readArguments ( argc , argv , & word , & phrase , & prime , pFileName ) ;

	if ( ! isArgumentsOk ) {

		for ( i = 1 ; i < numberOfProcess ; ++ i ) {

			/* Sinalize for the other processes to quit. */
			MPI_Send ( & isArgumentsOk , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		}

		return ;
	}

	for ( i = 1 ; i < numberOfProcess ; ++ i ) {

		MPI_Send ( & isArgumentsOk , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		MPI_Send ( & word , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		MPI_Send ( & phrase , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		MPI_Send ( & prime , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
	}

	readFile ( pFileName , & pFileBuffer , & fileBufferSize ) ;

	if ( word ) {

		findWordPalindromes ( numberOfProcess , pFileBuffer , fileBufferSize , prime ) ;
	}

	if ( phrase ) {

		findPhrasePalindromes ( numberOfProcess , pFileBuffer , fileBufferSize ) ;
	}

	free ( pFileBuffer ) ;
}

void regularProcess ( void ) {

	int word ; /* Flag to check for palindrome in words. */
	int phrase ; /* Flag to check for palindrome in phrases. */
	int prime ; /* Flag to check for prime numbers. */
	int isArgumentsOk ; /* Flag to indicate if the arguments from the command line are ok. */

	MPI_Status status ;

	MPI_Recv ( & isArgumentsOk , sizeof ( int ) , MPI_BYTE , 0 , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;

	if ( ! isArgumentsOk ) {

		return ;
	}

	MPI_Recv ( & word , sizeof ( int ) , MPI_BYTE , 0 , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;
	MPI_Recv ( & phrase , sizeof ( int ) , MPI_BYTE , 0 , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;
	MPI_Recv ( & prime , sizeof ( int ) , MPI_BYTE , 0 , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;

	if ( word ) {

		helpFindWordPalindromes ( prime ) ;
	}

	if ( phrase ) {

		helpFindPhrasePalindromes ( ) ;
	}
}

void findWordPalindromes ( int numberOfProcess , char * pFileBuffer , int fileBufferSize , int prime ) {
	
	int i ;
	int division ; /* How many words goes to each process. */
	int remainder ; /* Division remainder between the words and process. */

	int count = 0 ; /* How many words are palindromes. */
	int countReceive = 0 ; /* How many palindromes did the other processes found. */
	int countPrime = 0 ; /* How many palindromes have its sum equal to a prime. */

	char * pWordBuffer ; /* Buffer with words. */
	int wordNumber ; /* Number of words. */
	int wordSize ; /* Max number of characters in a word. */

	int wordBufferSizeSend ; /* The size of the buffer with words that will be sent. */
	char * pWordBufferSend ; /* The address of the first word that the other processes will process. */

	MPI_Status status ;

	/* Get the words */

	getWords ( pFileBuffer , fileBufferSize , & pWordBuffer , & wordNumber , & wordSize ) ;

	/* Divide the words. */

	division = wordNumber / numberOfProcess ;
	remainder = wordNumber % numberOfProcess ;
	pWordBufferSend = pWordBuffer ;

	for ( i = 1 ; i < numberOfProcess ; ++ i ) {

		pWordBufferSend += wordSize * division ;
		wordBufferSizeSend = wordSize * division ;

		if ( i != 1 && remainder ) {

			pWordBufferSend += wordSize ;
			wordBufferSizeSend += wordSize ;
			-- remainder ;
		}

		MPI_Send ( & wordBufferSizeSend , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		MPI_Send ( pWordBufferSend , wordBufferSizeSend , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		MPI_Send ( & wordSize , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
	}

	/* Processing its own share. */

	for ( i = 0 ; i < division ; ++ i ) {

		if ( isPalindrome ( pWordBuffer + i * wordSize ) ) {

			++ count ;

			if ( prime ) {

				countPrime += isPrime ( pWordBuffer + i * wordSize ) ;
			}
		}
	}

	/* Gather results. */

	for ( i = 1 ; i < numberOfProcess ; ++ i ) {

		MPI_Recv ( & countReceive , sizeof ( int ) , MPI_BYTE , i , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;
		count += countReceive ;

		if ( prime ) {

			MPI_Recv ( & countReceive , sizeof ( int ) , MPI_BYTE , i , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;
			countPrime += countReceive ;
		}
	}

	/* Show results. */

	if ( count == 1 ) {

		printf ( "There is only one word that is a palindrome.\n" ) ;
	}

	if ( count == 0 ) {

		printf ( "There isn't a word that is a palindrome.\n" ) ;
	}

	if ( count > 1 ) {

		printf ( "%d words are palindromes.\n" , count ) ;
	}

	if ( prime ) {

		if ( countPrime == 1 ) {

			printf ( "There is only one palindrome that have its sum equal to a prime number.\n" ) ;
		}

		if ( countPrime == 0 ) {

			printf ( "There isn't a palindrome that have its sum equal to a prime number.\n" ) ;
		}

		if ( countPrime > 1 ) {

			printf ( "%d palindrome have its sum equal to a prime number.\n" , countPrime ) ;
		}
	}

	free ( pWordBuffer ) ;
}

void helpFindWordPalindromes ( int prime ) {

	char * pWordBuffer ; /* Buffer with words to process. */
	int wordBufferSize ; /* The size of the buffer. */
	int wordSize ; /* Max number of characters in a word. */
	int count = 0 ; /* Number of palindromes. */
	int countPrime = 0 ; /* How many palindromes have its sum equal to a prime. */
	int i ;

	MPI_Status status ;

	/* Receive the size of the buffer. */

	MPI_Recv ( & wordBufferSize , sizeof ( int ) , MPI_BYTE , MPI_ANY_SOURCE , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;

	pWordBuffer = malloc ( wordBufferSize ) ;

	/* Receive the words. */

	MPI_Recv ( pWordBuffer , wordBufferSize , MPI_BYTE , MPI_ANY_SOURCE , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;
	MPI_Recv ( & wordSize , sizeof ( int ) , MPI_BYTE , MPI_ANY_SOURCE , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;

	/* Count palindromes. */

	for ( i = 0 ; i < wordBufferSize / wordSize ; ++ i ) {

		if ( isPalindrome ( pWordBuffer + i * wordSize ) ) {

			++ count ;

			if ( prime ) {

				countPrime += isPrime ( pWordBuffer + i * wordSize ) ;
			}
		}
	}

	/* Send results. */

	MPI_Send ( & count , sizeof ( int ) , MPI_BYTE , 0 , 0 , MPI_COMM_WORLD ) ;

	if ( prime ) {

		MPI_Send ( & countPrime , sizeof ( int ) , MPI_BYTE , 0 , 0 , MPI_COMM_WORLD ) ;
	}

	free ( pWordBuffer ) ;
}

void findPhrasePalindromes ( int numberOfProcess , char * pFileBuffer , int fileBufferSize ) {

	int i ;
	int division ; /* How many phrases goes to each process. */
	int remainder ; /* Division remainder between the phrases and process */

	int count = 0 ; /* How many phrases are palindromes. */
	int countReceive = 0 ; /* How many palindromes did the other processes found. */

	char * pPhraseBuffer ; /* Buffer with phrases. */
	int phraseNumber ; /* Number of phrases. */
	int phraseSize ; /* Max number of characters in a phrase. */

	int phraseBufferSizeSend ; /* The size of the buffer with phrases that will be sent. */
	char * pPhraseBufferSend ; /* The address of the first phrase that the other processes will process. */

	MPI_Status status ;

	/* Get the phrases */

	getPhrases ( pFileBuffer , fileBufferSize , & pPhraseBuffer , & phraseNumber , & phraseSize ) ;

	/* Divide the phrases. */

	division = phraseNumber / numberOfProcess ;
	remainder = phraseNumber % numberOfProcess ;
	pPhraseBufferSend = pPhraseBuffer ;

	for ( i = 1 ; i < numberOfProcess ; ++ i ) {

		pPhraseBufferSend += phraseSize * division ;
		phraseBufferSizeSend = phraseSize * division ;

		if ( i != 1 && remainder ) {

			pPhraseBufferSend += phraseSize ;
			phraseBufferSizeSend += phraseSize ;
			-- remainder ;
		}

		MPI_Send ( & phraseBufferSizeSend , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		MPI_Send ( pPhraseBufferSend , phraseBufferSizeSend , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
		MPI_Send ( & phraseSize , sizeof ( int ) , MPI_BYTE , i , 0 , MPI_COMM_WORLD ) ;
	}

	/* Process its own share. */

	for ( i = 0 ; i < division ; ++ i ) {

		count += isPalindrome ( pPhraseBuffer + i * phraseSize ) ; 
	}

	/* Gather results. */

	for ( i = 1 ; i < numberOfProcess ; ++ i ) {

		MPI_Recv ( & countReceive , sizeof ( int ) , MPI_BYTE , i , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;
		count += countReceive ;
	}

	/* Show results. */

	if ( count == 1 ) {

		printf ( "There is only one phrase that is a palindrome.\n" ) ;
	}

	if ( count == 0 ) {

		printf ( "There isn't a phrase that is a palindrome.\n" ) ;
	}

	if ( count > 1 ) {

		printf ( "%d phrases are palindromes.\n" , count ) ;
	}

	free ( pPhraseBuffer ) ;
}

void helpFindPhrasePalindromes ( void ) {

	char * pPhraseBuffer ; /* Buffer with phrases to process. */
	int phraseBufferSize ; /* The size of the buffer. */
	int phraseSize ; /* Max number of characters in a phrase. */
	int count = 0 ; /* Number of palindromes. */
	int i ;

	MPI_Status status ;

	/* Receive the size of the buffer. */

	MPI_Recv ( & phraseBufferSize , sizeof ( int ) , MPI_BYTE , MPI_ANY_SOURCE , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;

	pPhraseBuffer = malloc ( phraseBufferSize ) ;

	/* Receive the phrases. */

	MPI_Recv ( pPhraseBuffer , phraseBufferSize , MPI_BYTE , MPI_ANY_SOURCE , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;
	MPI_Recv ( & phraseSize , sizeof ( int ) , MPI_BYTE , MPI_ANY_SOURCE , MPI_ANY_TAG , MPI_COMM_WORLD , & status ) ;

	/* Count palindromes. */

	for ( i = 0 ; i < phraseBufferSize / phraseSize ; ++ i ) {

		count += isPalindrome ( pPhraseBuffer + i * phraseSize ) ; 
	}

	/* Send results. */

	MPI_Send ( & count , sizeof ( int ) , MPI_BYTE , 0 , 0 , MPI_COMM_WORLD ) ;

	free ( pPhraseBuffer ) ;
}

